#include "terrain.h"

Terrain::Terrain()
{
   ground.filename = "resources/dirt.raw";
   ground.width = ground.height = 128;
   
   height.filename = "resources/height.raw";
   height.width = height.height = 128;
   
   sky.filename = "resources/sky.raw";
   sky.width = sky.height = 128;
}

void Terrain::Draw(Camera* currentCam)
{
   //    Lazy initalization of static members.
   if(!isInit)
   {
      Init();
      isInit = true;
   }
   
   //    Draws the sky
   makeSkyBox(currentCam);
   //    Draws the Terrain
   makeGround();
}
void Terrain::makeGround()
{
   glPushMatrix();
   {
      //    Moves the terrain in the XZ plane to center the howitzer.
      //    Moves the terrain in Y plane to adjust for the scaling.
      glTranslatef(-512, -80/10, -256);
      glScalef(10, 80, 10);
      
      //    Make sure that the correct face is showing and that the
      //    texturing is un-colored
      //glColor3fv(White);
      glFrontFace(GL_CW);
      
      glEnable(GL_TEXTURE_2D);
      glBindTexture(GL_TEXTURE_2D, ground.id);
      
      // Sets the specular and shininess of the object.
      GLfloat spec[4] = {1.0,0.0,0.0,1.0};
      GLfloat amb[4] = {0.75,0.65,0.7,1.0};
      GLfloat dif[4] = {0.6,1.0,0.2,1.0};
      glMaterialfv(GL_FRONT, GL_SPECULAR, spec);
      glMateriali(GL_FRONT, GL_SHININESS, 128);
      glMaterialfv(GL_FRONT, GL_AMBIENT, amb);
      glMaterialfv(GL_FRONT, GL_DIFFUSE, dif);
      
      // Stores the calculated normal
      M3DVector3f vNormal;
      
      int u,v, repeat = 4;
      for (int i = 0; i < height.width - 1; ++i)
      {
         u = i % repeat;
         glBegin(GL_TRIANGLES);
         for (int j = 0; j < height.height - 1; ++j)
         {
            v = j % repeat;
            
            {
               M3DVector3f vPoints[3] = {{ i, height.map[i][j], j},
                  { i+1,  height.map[i+1][j], j },
                  { i+1,  height.map[i+1][j+1],  j+1 }};
               
               m3dFindNormal(vNormal, vPoints[0], vPoints[1], vPoints[2]);
               glNormal3fv(vNormal);
               
               glTexCoord2f(float(u)/repeat, float(v)/repeat);
               glVertex3fv(vPoints[0]);
               glTexCoord2f(float(u + 1)/repeat, float(v)/repeat);
               glVertex3fv(vPoints[1]);
               glTexCoord2f(float(u + 1)/repeat, float(v + 1)/repeat);
               glVertex3fv(vPoints[2]);
            }
            
            {
               M3DVector3f vPoints[3] = {{ i, height.map[i][j], j },
                  { i+1,  height.map[i+1][j+1], j+1 },
                  { i, height.map[i][j+1], j+1 }};
               
               m3dFindNormal(vNormal, vPoints[0], vPoints[1], vPoints[2]);
               glNormal3fv(vNormal);
               
               glTexCoord2f(float(u)/repeat, float(v)/repeat);
               glVertex3fv(vPoints[0]);
               glTexCoord2f(float(u + 1)/repeat, float(v + 1)/repeat);
               glVertex3fv(vPoints[1]);
               glTexCoord2f(float(u)/repeat, float(v + 1)/repeat);
               glVertex3fv(vPoints[2]);
            }
         }
         glEnd();
      }
      
      //    Unbinds the texture from GL_TEXTURE_2D
      glBindTexture(GL_TEXTURE_2D, 0);
	   glDisable(GL_TEXTURE_2D);
   }
   glPopMatrix();
}

void Terrain::makeSkyBox(Camera* currentCam)
{
   //    Draws the skybox around the camera without depth testing.
   float* offset = currentCam->GetLocation();
   glPushMatrix();
   {
      // Sets the specular and shininess of the object.
      GLfloat spec[4] = {1.0,1.0,1.0,1.0};
      GLfloat amb[4] = {0.70,0.8,1.0,1.0};
      GLfloat dif[4] = {0.75,0.75,1.0,1.0};
      glMaterialfv(GL_FRONT, GL_SPECULAR, spec);
      glMateriali(GL_FRONT, GL_SHININESS, 128);
      glMaterialfv(GL_FRONT, GL_AMBIENT, amb);
      glMaterialfv(GL_FRONT, GL_DIFFUSE, dif);
      
      glDisable(GL_DEPTH_TEST);
      glEnable(GL_TEXTURE_2D);
      glBindTexture(GL_TEXTURE_2D, sky.id);
      
      glBegin(GL_QUADS);
      //glColor3fv(White);
      
      glNormal3f(0.0f, 0.0f, 1.0f);
      glTexCoord2f(0.0f, 1.0f);  glVertex3f(offset[x] + 50, offset[y] + -5, offset[z] + 50);
      glTexCoord2f(1.0f, 1.0f);  glVertex3f(offset[x] + -50, offset[y] + -5, offset[z] + 50);
      glTexCoord2f(1.0f, 0.0f); glVertex3f(offset[x] + -50, offset[y] + 50, offset[z] + 50);
      glTexCoord2f(0.0f, 0.0f); glVertex3f(offset[x] + 50, offset[y] + 50, offset[z] + 50);
      
      glNormal3f(0.0f, 0.0f, -1.0f);
      glTexCoord2f(0.0f, 1.0f);  glVertex3f(offset[x] + -50, offset[y] + -5, offset[z] + -50);
      glTexCoord2f(1.0f, 1.0f);  glVertex3f(offset[x] + 50, offset[y] + -5, offset[z] + -50);
      glTexCoord2f(1.0f, 0.0f); glVertex3f(offset[x] + 50, offset[y] + 50, offset[z] + -50);
      glTexCoord2f(0.0f, 0.0f); glVertex3f(offset[x] + -50, offset[y] + 50, offset[z] + -50);
      
      glNormal3f(1.0f, 0.0f, 0.0f);
      glTexCoord2f(0.0f, 1.0f);  glVertex3f(offset[x] + 50, offset[y] + -5, offset[z] + -50);
      glTexCoord2f(1.0f, 1.0f);  glVertex3f(offset[x] + 50, offset[y] + -5, offset[z] + 50);
      glTexCoord2f(1.0f, 0.0f); glVertex3f(offset[x] + 50, offset[y] + 50, offset[z] + 50);
      glTexCoord2f(0.0f, 0.0f); glVertex3f(offset[x] + 50, offset[y] + 50, offset[z] + -50);
      
      glNormal3f(-1.0f, 0.0f, 0.0f);
      glTexCoord2f(0.0f, 1.0f);  glVertex3f(offset[x] + -50, offset[y] + -5, offset[z] + 50);
      glTexCoord2f(1.0f, 1.0f);  glVertex3f(offset[x] + -50, offset[y] + -5, offset[z] + -50);
      glTexCoord2f(1.0f, 0.0f); glVertex3f(offset[x] + -50, offset[y] + 50, offset[z] + -50);
      glTexCoord2f(0.0f, 0.0f); glVertex3f(offset[x] + -50, offset[y] + 50, offset[z] + 50);
      
      glNormal3f(0.0f, -1.0f, 0.0f);
      glTexCoord2f(0.0f, 0.0f);  glVertex3f(offset[x] + 50, offset[y] + 50, offset[z] + 50);
      glTexCoord2f(1.0f, 0.0f);  glVertex3f(offset[x] + -50, offset[y] + 50, offset[z] + 50);
      glTexCoord2f(1.0f, 0.3f); glVertex3f(offset[x] + -50, offset[y] + 50, offset[z] + -50);
      glTexCoord2f(0.0f, 0.3f); glVertex3f(offset[x] + 50, offset[y] + 50, offset[z] + -50);
      
      glEnd();
      glBindTexture(GL_TEXTURE_2D, 0);
      glDisable(GL_TEXTURE_2D);
      
      glEnable(GL_DEPTH_TEST);
   }
   glPopMatrix();
}
void Terrain::Init()
{
   // Set up texture maps        
   loadRawImage(ground);
   loadRawImage(sky);
   makeTexture(ground);
   makeTexture(sky);
   
   getHeightMap(height);
}
